home *** CD-ROM | disk | FTP | other *** search
- /* TunerApp.m -- copyright 1992 by C.D.Lane */
-
- #import "TunerApp.h"
-
- #import <dsp/arrayproc.h>
-
- #define APPLICATION "Tuner"
- #define VERSION __DATE__
-
- #define RTF_SEGMENT "__RTF"
- #define HELP_SECTION "Tuner.rtf"
-
- #define freq(k) MKKeyNumToFreq(k)
- #define keyNum(f) MKFreqToKeyNum(f, NULL, 0.0)
-
- #define writeDefault(n, v) NXWriteDefault(APPLICATION, n, v)
- #define getStringDefault(n) NXGetDefaultValue(APPLICATION, n)
- #define getIntDefault(n) atoi(NXGetDefaultValue(APPLICATION, n))
- #define getFloatDefault(n) atof(NXGetDefaultValue(APPLICATION, n))
-
- #define TONES (12)
-
- #define RECORDDELAY (0.25)
-
- #define FFT_SIZE (1024)
-
- #define FFT_DATA DSPAPGetLowestAddressXY()
- #define FFT_COEF (FFT_DATA + FFT_SIZE)
- #define FFT_SKIP (1)
- #define DATA_SKIP (2)
-
- #define FFT_IMAG DSPMapPMemY(FFT_DATA)
- #define FFT_REAL DSPMapPMemX(FFT_DATA)
-
- #define SIN_TABLE DSPMapPMemY(FFT_COEF)
- #define COS_TABLE DSPMapPMemX(FFT_COEF)
-
- typedef enum {ZERO = 0, FFT} METHODS;
- typedef enum {LINEAR = -1, INDEXED = 0} ADDR_MODES;
-
- @implementation TunerApp : Application
-
- void stopRecord(DPSTimedEntry timedEntry, double now, id self)
- {
- [self stop:self];
- }
-
- void startRecord(DPSTimedEntry timedEntry, double now, id self)
- {
- int status;
-
- if((status = [self record]) != SND_ERR_NONE) [NXApp printf:"record: %s\n", SNDSoundError(status)];
- }
-
- - appDidInit:sender
- {
- [defaults registerDefaults:APPLICATION];
-
- [[[[soundView setAutoscale:YES] setDisplayMode:SK_DISPLAY_WAVE] setAutodisplay:YES] setSound:sound];
-
- key = lastKey = c00k;
- #ifdef DEBUG
- (void) DSPSetErrorFP(stderr);
- (void) DSPEnableErrorLog();
- #endif
- [self loadDefaults:sender];
-
- timedEntry = DPSAddTimedEntry(RECORDDELAY, (DPSTimedEntryProc) &startRecord, sound, NX_BASETHRESHOLD);
-
- return self;
- }
-
- - free
- {
- if(timedEntry != NULL) DPSRemoveTimedEntry(timedEntry);
- #ifdef DEBUG
- (void) DSPDSPDisableErrorLog();
- #endif
- return [super free];
- }
-
- - willRecord:sender
- {
- [soundMeter run:sender];
-
- if(timedEntry != NULL) DPSRemoveTimedEntry(timedEntry);
-
- timedEntry = DPSAddTimedEntry([timeSlider floatValue], (DPSTimedEntryProc) &stopRecord, sound, NX_BASETHRESHOLD);
-
- return self;
- }
-
- - didRecord:sender
- {
- id result = nil;
- int status = SND_ERR_NONE;
-
- if(timedEntry != NULL) DPSRemoveTimedEntry(timedEntry);
- timedEntry = NULL;
-
- amplitude = [[soundMeter stop:sender] peakValue];
-
- if((status = [sample copySound:sound]) == SND_ERR_NONE) {
- if((status = [sound deleteSamples]) == SND_ERR_NONE) {
- if(amplitude >= [squelchSlider floatValue]) {
- if((status = [sample convertToFormat:SND_FORMAT_LINEAR_16]) == SND_ERR_NONE) {
- switch([[methodMatrix selectedCell] tag]) {
- case FFT : result = [self computeFrequencyViaFFT]; break;
- case ZERO : default : result = [self computeFrequency]; break;
- }
- }
- else [self printf:"convertToFormat: %s\n", SNDSoundError(status)];
- }
- else [[self clearNoteName] clearCentError];
- }
- else [self printf:"deleteSamples: %s\n", SNDSoundError(status)];
- }
- else [self printf:"copySound: %s\n", SNDSoundError(status)];
-
- if(result != nil) {
- frequency *= [adjustmentSlider floatValue];
- #ifdef DEBUG
- [self printf:"amplitude = %f\n", amplitude];
- [self printf:"frequency = %f\n\n", frequency];
- #endif
- if((key = keyNum(frequency)) % TONES == lastKey) [[self showNoteName] showCentError];
- else [[self clearNoteName] clearCentError];
-
- lastKey = key % TONES;
- }
-
- timedEntry = DPSAddTimedEntry(RECORDDELAY, (DPSTimedEntryProc) &startRecord, sound, NX_BASETHRESHOLD);
-
- return self;
- }
-
- - windowDidBecomeKey:sender
- {
- int size;
- void *pointer;
- NXStream *stream;
- static BOOL flag = NO;
-
- if(flag) return self;
-
- if((pointer = getsectdata(RTF_SEGMENT, HELP_SECTION, &size)) == NULL) return nil;
-
- if((stream = NXOpenMemory((const char *) pointer, size, NX_READONLY)) == NULL) return nil;
-
- [[helpScrollView docView] readRichText:stream];
-
- NXCloseMemory(stream, NX_FREEBUFFER);
-
- flag = YES;
-
- return self;
- }
-
- - computeFrequency
- {
- short *pointer = (short *) [sample data];
- unsigned int start, end = 0, i = 0, transitions = 0, size = [sample sampleCount];
-
- while(i < size && pointer[end = i++] == 0);
-
- while(i < size && !((pointer[end] > 0 && pointer[i] < 0) || (pointer[end] < 0 && pointer[i] > 0))) ++i;
-
- end = i++;
-
- if(i >= size) return nil;
-
- for(start = i; i < size; i++)
- if((pointer[end] > 0 && pointer[i] < 0) || (pointer[end] < 0 && pointer[i] > 0)) {
- transitions++;
- end = i;
- }
-
- if(start > end) return nil;
-
- frequency = (transitions * [sample samplingRate]) / (2 * ((end - start) + 1));
-
- return self;
- }
-
- - computeFrequencyViaFFT
- {
- unsigned int i;
- MKKeyNum note, maximum = c00k;
- float pitch, pitches[b7k], spectrum[FFT_SIZE];
- short data[FFT_SIZE], hits[b7k], *pointer = (short *) [sample data];
- float threshold = [squelchSlider floatValue], rate = [sample samplingRate] / (FFT_SIZE * DATA_SKIP);
-
- if([sample sampleCount] < (FFT_SIZE * DATA_SKIP)) return nil;
-
- for(i = 0; i < FFT_SIZE; i++) data[i] = pointer[i * DATA_SKIP];
-
- for(note = c00k; note < b7k; note++) pitches[note] = hits[note] = 0;
-
- if(DSPAPInit() == 0) {
- (void) DSPAPWriteFloatArray(DSPAPSinTable(FFT_SIZE), SIN_TABLE, 1, FFT_SIZE/2);
- (void) DSPAPWriteFloatArray(DSPAPCosTable(FFT_SIZE), COS_TABLE, 1, FFT_SIZE/2);
-
- (void) DSPAPWriteShortArray(data, FFT_REAL, 1, FFT_SIZE);
-
- (void) DSPAPvclear(FFT_IMAG, 1, FFT_SIZE);
-
- (void) DSPAPfftr2a(FFT_SIZE, FFT_DATA, FFT_COEF);
-
- (void) DSPSetDMAReadMReg(INDEXED); {
- (void) DSPAPReadFloatArray(spectrum, FFT_IMAG, FFT_SIZE/2, FFT_SIZE);
- } (void) DSPSetDMAReadMReg(LINEAR);
-
- (void) DSPAPFree();
- }
- else return [self switchMethod:"Switching methods, DSP not available!"];
-
- for(i = 0; i < FFT_SIZE; i++)
- if(fabs(spectrum[i]) >= threshold) {
- note = keyNum(pitch = (i * rate));
- pitches[note] += pitch;
- ++hits[note];
- }
-
- for(note = c00k; note < b7k; note++) if(hits[note] > hits[maximum]) maximum = note;
-
- if(hits[maximum] == 0) return nil;
-
- frequency = pitches[maximum] / hits[maximum];
-
- return self;
- }
-
- - showNoteName
- {
- unsigned int i, size;
- id control, list = [buttonMatrix cellList];
-
- size = [list count];
-
- for(i = 0; i < size; i++) {
- control = [list objectAt:i];
- [control setEnabled:(((key + [transpositionField intValue]) % TONES) == [control tag])];
- }
-
- return self;
- }
-
- - clearNoteName
- {
- unsigned int i, size;
- id list = [buttonMatrix cellList];
-
- size = [list count];
-
- for(i = 0; i < size; i++) [[list objectAt:i] setEnabled:NO];
-
- return self;
- }
-
- - showCentError
- {
- double error, zero, minimum, maximum, correct, calibration = [calibrationField floatValue] / freq(a4k);
-
- correct = freq(key) * calibration;
- minimum = freq(key - 1) * calibration;
- maximum = freq(key + 1) * calibration;
-
- [[[centSlider setMinValue:minimum] setMaxValue:maximum] setFloatValue:frequency];
-
- [centSlider setEnabled:YES];
-
- zero = (((error = frequency - correct) > 0.0) ? maximum - correct : correct - minimum) * [toleranceSlider floatValue];
-
- [flat setState:(error <= zero)];
- [sharp setState:(error >= -zero)];
- [attune setState:([flat state] && [sharp state])];
-
- return self;
- }
-
- - clearCentError
- {
- [[[[centSlider setMinValue:freq(af4k)] setMaxValue:freq(as4k)] setFloatValue:freq(a4k)] setEnabled:NO];
-
- [flat setState:NO];
- [sharp setState:NO];
- [attune setState:NO];
-
- return self;
- }
-
- - switchMethod:(const char *) reason
- {
- (void) NXRunAlertPanel(APPLICATION, reason, NULL, NULL, NULL);
-
- [methodMatrix selectCellWithTag:ZERO];
-
- return nil;
- }
-
- - setDefault:sender
- {
- [sender setTag:YES];
-
- return self;
- }
-
- - setCalibration:sender
- {
- [calibrationField setIntValue:[[sender selectedCell] tag]];
-
- [sender setTag:YES];
-
- return self;
- }
-
- - setTransposition:sender
- {
- [transpositionField setIntValue:[[sender selectedCell] tag]];
-
- [sender setTag:YES];
-
- return self;
- }
-
- - printf:(const char *) format, ...
- {
- va_list ap;
-
- va_start(ap, format); {
- (void) vfprintf(stderr, format, ap);
- } va_end(ap);
-
- return self;
- }
-
- - loadDefaults:sender;
- {
- const char *string;
- unsigned int i, size;
- id cell, list = [methodMatrix cellList];
-
- [[squelchSlider setTag:NO] setFloatValue:getFloatDefault("Squelch")];
- [[timeSlider setTag:NO] setFloatValue:getFloatDefault("SampleTime")];
- [[toleranceSlider setTag:NO] setFloatValue:getFloatDefault("Tolerance")];
- [[adjustmentSlider setTag:NO] setFloatValue:getFloatDefault("Adjustment")];
- [calibrationField setIntValue:getIntDefault("Calibration")];
- [[calibrationMatrix setTag:NO] selectCellWithTag:[calibrationField intValue]];
- [transpositionField setIntValue:getIntDefault("Transposition")];
- [[transpositionMatrix setTag:NO] selectCellWithTag:[transpositionField intValue]];
-
- for(i = 0, size = [list count], string = getStringDefault("Method"); i < size; i++)
- if(strcmp([(cell = [list objectAt:i]) title], string) == 0) [methodMatrix selectCell:cell];
- [methodMatrix setTag:NO];
-
- return self;
- }
-
- - saveDefaults:sender
- {
- if([squelchSlider tag]) (void) writeDefault("Squelch", [squelchSlider stringValue]);
- if([timeSlider tag]) (void) writeDefault("SampleTime", [timeSlider stringValue]);
- if([toleranceSlider tag]) (void) writeDefault("Tolerance", [toleranceSlider stringValue]);
- if([adjustmentSlider tag]) (void) writeDefault("Adjustment", [adjustmentSlider stringValue]);
- if([calibrationMatrix tag]) (void) writeDefault("Calibration", [calibrationField stringValue]);
- if([transpositionMatrix tag]) (void) writeDefault("Transposition", [transpositionField stringValue]);
- if([methodMatrix tag]) (void) writeDefault("Method", [[methodMatrix selectedCell] title]);
-
- return self;
- }
-
- - resetDefaults:sender
- {
- [defaults updateDefaults];
-
- return [self loadDefaults:sender];
- }
-
- - setVersion:anObject
- {
- [(version = anObject) setStringValue:VERSION];
-
- return self;
- }
-
- @end
-
- @implementation Sound(SimpleMethods)
-
- - (int) convertToFormat:(int) aFormat
- {
- return [self convertToFormat:aFormat samplingRate:[self samplingRate] channelCount:[self channelCount]];
- }
-
- @end
-
- @implementation NXStringTable(DefaultsTable)
-
- - registerDefaults:(const char *) owner
- {
- if(![self applyToDefaults:owner function:&NXRegisterDefaults]) return nil;
-
- return self;
- }
-
- - writeDefaults:(const char *) owner
- {
- if(![self applyToDefaults:owner function:&NXWriteDefaults]) return nil;
-
- return self;
- }
-
- - updateDefaults
- {
- NXUpdateDefaults();
-
- return self;
- }
-
- - (int) applyToDefaults:(const char *) owner function:(int (*)(const char *, const NXDefaultsVector)) routine
- {
- int i, status;
- const void *key, *value;
- struct _NXDefault *vector;
- NXHashState state = [self initState];
-
- if((vector = (struct _NXDefault *) calloc((size_t) ([self count] + 1), sizeof(struct _NXDefault))) == NULL) perror("calloc");
-
- for(i = 0; [self nextState:&state key:&key value:&value]; i++) {
- vector[i].name = (char *) key;
- vector[i].value = (char *) value;
- }
-
- vector[i].name = vector[i].value = NULL;
-
- status = (*routine)(owner, vector);
-
- cfree(vector);
-
- return status;
- }
-
- @end
-